// wsuconv.cpp - version 1.03
// NCC - Copyright (C) 2012 Willow Schlanger <wschlanger@gmail.com>
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list
//    of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
//    of conditions and the following disclaimer in the documentation and/or other materials
//    provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "../util/ncc_util.h"

#include <stdexcept>
#include <iostream>

void print_banner()
{
	std::cout << "WSUCONV      Converts between UTF-8, UTF-16, and Standard ASCII" << std::endl;
}

void print_basic_copyright()
{
	std::cout << "Copyright (C) 2012 Willow Schlanger (use --about for more info)\n";
}

void print_usage()
{
	std::cout << "Usage: wsuconv [options] infile outfile\n";
}

void print_help()
{
	print_banner();
	print_basic_copyright();
	std::cout << "\n";
	print_usage();
	std::cout << "\n";
	std::cout << "options:\n";
	std::cout << "--about   print legal information and author contact e-mail(s)\n";
	std::cout << "--help    print this help message\n";
	std::cout << "--omit-bom\n";
	std::cout << "  Normally when we generate UTF outputs, we write a Byte Order Mark.\n";
	std::cout << "  To produce output without a BOM, use this option. Note that if the\n";
	std::cout << "  output format is Standard ASCII, a BOM is never written.\n";
	std::cout << "  NOTE: if the input and output format are both UTF-8, a BOM is written\n";
	std::cout << "        only if present in the source.\n";
	std::cout << "--in=x\n";
	std::cout << "  Normally we auto-detect the input format based on its Byte Order Mark\n";
	std::cout << "  and we assume the input is UTF-8 if no such mark is present. You can\n";
	std::cout << "  use this option to force the input format to UTF-16LE or UTF-16BE\n";
	std::cout << "  (useful when the source is in UNICODE but has no Byte Order Mark).\n";
	std::cout << "  x should be one of these: std-ascii utf-16le utf-16be utf-8\n";
	std::cout << "  If --in=utf-8 is used, the input may still begin with a UTF-8 Byte\n";
	std::cout << "  Order Mark. If --in=utf-16le or --in=utf-16be is used and the input\n";
	std::cout << "  begins with a UTF-16 Byte Order Mark, the endianness of the UTF-16\n";
	std::cout << "  will be determined from the Byte Order Mark, thus overriding the\n";
	std::cout << "  'le' or 'be' (little endian or big endian) format specified here.\n";
	std::cout << "--out=x\n";
	std::cout << "  Specify the output format (by default we generate UTF-8 output).\n";
	std::cout << "  x should be one of these: std-ascii utf-16le utf-16be utf-8\n";
	std::cout << "--newline=x\n";
	std::cout << "  This option controls the output line-ending format. The default\n";
	std::cout << "  behavior is to preserve CR and LF characters as found in the input.\n";
	std::cout << "  x should be one of: default lf crlf cr\n";
	std::cout << "--fail-on-errors\n";
	std::cout << "  Normally we ignore most error conditions, such as invalid UNICODE\n";
	std::cout << "  characters. When this happens, a replacement character (U+FFFD)\n";
	std::cout << "  is generated except for Standard ASCII mode where a '?' character\n";
	std::cout << "  is generated. If --fail-on-errors is specified, we will generate\n";
	std::cout << "  an error message, abort, and delete the output file if any errors\n";
	std::cout << "  are found with the input." << std::endl;
}

void print_about()
{
	print_banner();
	std::cout << "Copyright (C) 2012 Willow Schlanger      <wschlanger@gmail.com>\n";
	std::cout << "\n";
	std::cout << "Redistribution and use in source and binary forms, with or without\n";
	std::cout << "modification are permitted provided that the following conditions are met:\n";
	std::cout << "\n";
	std::cout << "1. Redistributions of source code must retain the above copyright notice,\n";
	std::cout << "   this list of conditions and the following disclaimer.\n";
	std::cout << "2. Redistributions in binary form must reproduce the above copyright notice,\n";
	std::cout << "   this list of conditions and the following disclaimer in the documentation\n";
	std::cout << "   and/or other materials provided with the distribution.\n";
	std::cout << "\n";
	std::cout << "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n";
	std::cout << "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n";
	std::cout << "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n";
	std::cout << "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n";
	std::cout << "ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n";
	std::cout << "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n";
	std::cout << "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n";
	std::cout << "ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n";
	std::cout << "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n";
	std::cout << "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." << std::endl;
}

// sample function to illustrate how to use the above code.
void write_utf16le_from_utf32(const char *ofname, NetwideCompilerCollection::NccRawCharacterSource &src2, FILE *fo = NULL)
{
	using namespace NetwideCompilerCollection;
	using namespace std;
	
	if(src2.format() != NccRawCharacterSource::FORMAT_UTF32)
		throw std::runtime_error("source is not UTF-32");

	NccUtf16FromUtf32CharacterSource src(src2);

	if(!src2.status().empty())
	{
		throw std::runtime_error(std::string("encountered error: ") + src.status());
	}
	
	if(fo == NULL)
	{
		fo = NccCommonUtils::universalOpen(ofname, "wb");
		if(fo == NULL)
			throw std::runtime_error(std::string("unable to open file for writing: ") + std::string(ofname));
	}
	
	// Write UTF-16-LE Byte Order Mark.
	fputc(0xff, fo);
	fputc(0xfe, fo);

	// While we get valid characters, send them to the output file.
	while(src.current() >= 0 && src.current() <= 0xffff)
	{
		// note: even if src.current() is '\r' or '\n', just copy to output.
		// for the purpose of this sample we don't want to normalize or convert CR/LF's.
		fputc(src.current() & 0xff, fo);
		fputc((src.current() >> 8) & 0xff, fo);
		src.next();
	}
	
	fclose(fo);
	if(src.current() != NCC_STATUS_EOS)
	{
		// -1 means EOS. -2 means ERROR.
		if(src.current() == NCC_STATUS_ERROR && !src.status().empty())
		{
			throw std::runtime_error(std::string("encountered error: ") + src.status());
		}
		throw std::runtime_error("internal error (6)");
	}
}

void write_utf16be_from_utf32(const char *ofname, NetwideCompilerCollection::NccRawCharacterSource &src2)
{
	using namespace NetwideCompilerCollection;
	using namespace std;
	
	if(src2.format() != NccRawCharacterSource::FORMAT_UTF32)
		throw std::runtime_error("source is not UTF-32");

	NccUtf16FromUtf32CharacterSource src(src2);

	if(!src2.status().empty())
	{
		throw std::runtime_error(std::string("encountered error: ") + src.status());
	}
	
	FILE *fo = fopen(ofname, "wb");
	if(fo == NULL)
		throw std::runtime_error(std::string("unable to open file for writing: ") + std::string(ofname));
	
	// Write UTF-16-BE Byte Order Mark.
	fputc(0xfe, fo);
	fputc(0xff, fo);

	// While we get valid characters, send them to the output file.
	while(src.current() >= 0 && src.current() <= 0xffff)
	{
		// note: even if src.current() is '\r' or '\n', just copy to output.
		// for the purpose of this sample we don't want to normalize or convert CR/LF's.
		fputc((src.current() >> 8) & 0xff, fo);
		fputc(src.current() & 0xff, fo);
		src.next();
	}
	
	fclose(fo);
	if(src.current() != NCC_STATUS_EOS)
	{
		// -1 means EOS. -2 means ERROR.
		if(src.current() == NCC_STATUS_ERROR && !src.status().empty())
		{
			throw std::runtime_error(std::string("encountered error: ") + src.status());
		}
		throw std::runtime_error("internal error (6)");
	}
}

void write_utf8_from_utf32(const char *ofname, NetwideCompilerCollection::NccRawCharacterSource &src2)
{
	using namespace NetwideCompilerCollection;
	using namespace std;
	
	if(src2.format() != NccRawCharacterSource::FORMAT_UTF32)
		throw std::runtime_error("source is not UTF-32");

	NccUtf8FromUtf32CharacterSource src(src2);

	if(!src2.status().empty())
	{
		throw std::runtime_error(std::string("encountered error: ") + src.status());
	}
	
	FILE *fo = fopen(ofname, "wb");
	if(fo == NULL)
		throw std::runtime_error(std::string("unable to open file for writing: ") + std::string(ofname));
	
	// Write UTF-8 Byte Order Mark.
	fputc(0xef, fo);
	fputc(0xbb, fo);
	fputc(0xbf, fo);

	// While we get valid characters, send them to the output file.
	while(src.current() >= 0 && src.current() <= 0xff)
	{
		// note: even if src.current() is '\r' or '\n', just copy to output.
		// for the purpose of this sample we don't want to normalize or convert CR/LF's.
		fputc(src.current(), fo);
		src.next();
	}
	
	fclose(fo);
	if(src.current() != NCC_STATUS_EOS)
	{
		// -1 means EOS. -2 means ERROR.
		if(src.current() == NCC_STATUS_ERROR && !src.status().empty())
		{
			throw std::runtime_error(std::string("encountered error: ") + src.status());
		}
		throw std::runtime_error("internal error (6)");
	}
}

#if 0
class Utf32TestGenerator :
	public NetwideCompilerCollection::NccRawCharacterSource
{
	NetwideCompilerCollection::S4 curChar;
public:
	Utf32TestGenerator()
	{
		curChar = 0;
	}

	virtual ~Utf32TestGenerator() { }

	virtual NetwideCompilerCollection::S4 current()
	{
		return curChar;
	}

	virtual void next()
	{
		if(curChar < 0)
			return;
		++curChar;
		if(curChar == 0xd800)
			curChar = 0xe000;
		if(curChar == 0xfffe)
			curChar = 0x10000;
		if(curChar == 0x110000)
			curChar = -1;
	}

	virtual void close()
	{
		curChar = -1;
	}
	
	// This returns the current format.
	// If neither current() nor next() have been called yet, the file has not been
	// read yet. In that case, format() could cause a file read to get the BOM.
	virtual int format()
	{
		return FORMAT_UTF32;
	}
	
	virtual bool sourceHasBom()
	{
		return false;
	}
	
	virtual std::string status()
	{
		return "";
	}
};

void do_test_suite()
{
	using namespace std;
	using namespace NetwideCompilerCollection;
	
	std::cout << "Generating files... " << std::flush;
	{
		Utf32TestGenerator testSource;
		write_utf16le_from_utf32("utf16le_test_out.txt", testSource);
	}
	{
		Utf32TestGenerator testSource;
		write_utf16be_from_utf32("utf16be_test_out.txt", testSource);
	}
	{
		Utf32TestGenerator testSource;
		write_utf8_from_utf32("utf8_test_out.txt", testSource);
	}
	std::cout << "done" << std::endl;
	
	// Decode each file back to UTF-32 and compare.
	const char *filenames[] = {"utf16le_test_out.txt", "utf16be_test_out.txt", "utf8_test_out.txt", NULL};
	for(const char **fn = filenames; *fn != NULL; ++fn)
	{
		std::cout << "\nTranslating " << *fn << "..." << std::endl;
		
		NccCFileByteStream is(fopen(*fn, "rb"), false, false);

		// Create a generic UTF character source that reads from the byte stream.
		NccUtfCharacterSource src1(is);

		// Create a UTF-32 character source that reads from the generic UTF character source.
		// Fail on invalid characters, so we can remove them.
		NccUtf32CharacterSource src2(src1, true /* fail on invalid characters */);
		
		Utf32TestGenerator correct;
		S4 lastGood = -1;
		while(src2.current() >= 0)
		{
			lastGood = src2.current();
			if(lastGood != correct.current())
			{
				std::cout << "Discrepency found!!! Wanted: " << std::hex << correct.current() << std::dec << std::endl;
				break;
			}
			src2.next();
			correct.next();
		}
		std::cout << "Message = [" << src2.status() << "]" << std::endl;
		std::cout << std::hex << lastGood << std::dec << std::endl;
	}
}
#endif

namespace WsuInternal
{

using namespace std;
using namespace NetwideCompilerCollection;

class WsuConverter
{
	bool failOnInvalidChars;
	int out_utf_bits;
	bool out_big_endian;
	bool out_want_bom;
	const char *targetNewline;
	int inputMode;
public:
	WsuConverter()
	{
		failOnInvalidChars = false;
		out_utf_bits = 8;
		out_big_endian = false;
		out_want_bom = true;
		targetNewline = NULL;	// no new-line translation
		inputMode = NccUtfCharacterSource::INPUT_MODE_DEFAULT;
	}
	
	virtual ~WsuConverter()
	{
	}
	
	bool parseOptions(std::list<std::string> &options)
	{
		for(std::list<std::string>::iterator i = options.begin(); i != options.end(); ++i)
		{
			if(*i == "--omit-bom")
				out_want_bom = false;
			else
			if(*i == "--out=std-ascii")
				out_utf_bits = 7;
			else
			if(*i == "--out=utf-16le")
			{
				out_utf_bits = 16;
				out_big_endian = false;
			}
			else
			if(*i == "--out=utf-16be")
			{
				out_utf_bits = 16;
				out_big_endian = true;
			}
			else
			if(*i == "--out=utf-16le")
				out_utf_bits = 8;
			else
			if(*i == "--newline=lf")
				targetNewline = "\n";
			else
			if(*i == "--newline=crlf")
				targetNewline = "\r\n";
			else
			if(*i == "--newline=cr")
				targetNewline = "\r";
			else
			if(*i == "--fail-on-errors")
				failOnInvalidChars = true;
			else
			if(*i == "--in=std-ascii")
				inputMode = NccUtfCharacterSource::INPUT_MODE_ASCII7;
			else
			if(*i == "--in=utf-16le")
				inputMode = NccUtfCharacterSource::INPUT_MODE_UTF16_DEFAULT_LE;
			else
			if(*i == "--in=utf-16be")
				inputMode = NccUtfCharacterSource::INPUT_MODE_UTF16_DEFAULT_BE;
			else
			if(*i == "--in=utf-8")
				inputMode = NccUtfCharacterSource::INPUT_MODE_UTF8;
			else
			{
				std::cout << "ERROR: Invalid command-line argument: " << *i << std::endl;
				return false;
			}
		}
		
		return true;	// success
	}
	
	bool convert(FILE *fi, FILE *fo)
	{
		bool ok = true;

		// Create a byte stream that just reads from a file.
		// We are not responsible for closing the file.

		NccCFileByteStream is(fi, false, true /* leave open when done */);

		// Create a generic UTF character source that reads from the byte stream.
		NccUtfCharacterSource src1(is, inputMode);
		
		// Create a UTF-32 character source that reads from the generic UTF character source.
		NccUtf32CharacterSource src2(src1, failOnInvalidChars);
		
		if(src2.format() != NccRawCharacterSource::FORMAT_UTF32)
			throw std::runtime_error("internal UNICODE translation error");

		NccRawCharacterSource *src = NULL;
		NccRawCharacterSource *freeSrc = NULL;
		
		bool utf8SkipBom = false;
		if(out_utf_bits == 8)
		{
			freeSrc = src = new NccUtf8FromUtf32CharacterSource(src2);
			
			// If the source and output formats are both UTF-8 and the source does not begin with
			// a BOM, do not write a BOM to the output. This allows us to process Standard ASCII
			// files with default options (where we treat the input as UTF-8 and generate UTF-8
			// output). A user would not want a Standard ASCII file to get a UTF-8 BOM injected
			// at the beginning; nor do they want to have to use an option like --omit-bom for
			// this case.
			if(src->sourceFormat() == NccRawCharacterSource::FORMAT_UTF8 && !src->sourceHasBom())
				utf8SkipBom = true;
		}
		else
		if(out_utf_bits == 16)
			freeSrc = src = new NccUtf16FromUtf32CharacterSource(src2);
		else
		{
			// Output is Standard ASCII.
			src = &src2;
		}
		
		try
		{
			// Write any BOM.
			if(!utf8SkipBom)
				write_bom(fo);

			while(src->current() >= 0)
			{
				if(targetNewline != NULL && (src->current() == '\r' || src->current() == '\n'))
				{
					// Get (and count) any '\r' (CR) characters first.
					size_t num_cr = 0;
					while(src->current() == '\r')
					{
						++num_cr;
						src->next();
					}
					
					// Now see if we've got a '\n' (LF) character.
					bool lf_present = false;
					if(src->current() == '\n')
					{
						lf_present = true;
						src->next();
					}
					
					// CR, CR, LF -> one new line
					// CR, LF -> one new line
					// LF -> one new line
					// CR (without being followed by LF) -> as many new lines as there are CR's
					size_t num_newlines_needed = 1;
					if(!lf_present)
						num_newlines_needed = num_cr;
					
					// Generate the required number of new-lines in form the user wants.
					for(size_t i = 0; i < num_newlines_needed; ++i)
					{
						for(const char *p = targetNewline; *p != '\0'; ++p)
							doWrite(*p, fo);
					}
					
					continue;
				}
				
				doWrite(src->current(), fo);
				
				src->next();
			}
			
			// add error for version 1.01.
			if(src->current() != NCC_STATUS_EOS)
			{
				if(src->status().empty())
					std::cout << "ERROR: An unknown internal error has occurred" << std::endl;
				else
					std::cout << "ERROR: " << src->status() << std::endl;
				ok = false;
			}
		}
		catch(std::exception &e)
		{
			if(freeSrc != NULL)
			{
				delete freeSrc;
				freeSrc = NULL;
			}
			throw e;
		}
		
		if(freeSrc != NULL)
		{
			delete freeSrc;
			freeSrc = NULL;
		}
		
		return ok;
	}

private:
	// Note: it would be best if we checked that the fputc()'s used here
	// actually succeeded in writing. It is safe to throw an exception if
	// the write fails.
	void doWrite(S4 chr, FILE *fo)
	{
		if(out_utf_bits == 7)
		{
			if(chr >= 0 && chr <= 127)
				fputc(chr, fo);
			else
			if(!failOnInvalidChars)
				fputc('?', fo);
			else
			if(chr >= 128 && chr <= 255)
			{
				std::stringstream ss;
				ss << std::hex << static_cast<U4>(chr) << std::dec;
				throw std::runtime_error(std::string("attempted to generate character 0x") + ss.str() + std::string(" but output format is set to Standard ASCII"));
			}
			else
				throw std::runtime_error("Internal UNICODE conversion error (1)");
		}
		else
		if(out_utf_bits == 8)
		{
			if(chr >= 0 && chr <= 255)
				fputc(chr, fo);
			else
				throw std::runtime_error("Internal UNICODE conversion error (2)");
		}
		else
		if(out_utf_bits == 16)
		{
			if(chr >= 0 && chr <= 65535)
			{
				if(out_big_endian)
				{
					fputc((chr >> 8) & 0xff, fo);
					fputc((chr >> 0) & 0xff, fo);
				}
				else
				{
					fputc((chr >> 0) & 0xff, fo);
					fputc((chr >> 8) & 0xff, fo);
				}
			}
			else
				throw std::runtime_error("Internal UNICODE conversion error (3)");
		}
		else
			throw std::runtime_error("Internal UNICODE conversion error (4)");
	}

	void write_bom(FILE *fo)
	{
		if(out_want_bom)
		{
			if(out_utf_bits == 8)
			{
				// Write UTF-8 Byte Order Mark.
				fputc(0xef, fo);
				fputc(0xbb, fo);
				fputc(0xbf, fo);
			}
			else
			if(out_utf_bits == 16)
			{
				if(out_big_endian)
				{
					// Write UTF-16-BE Byte Order Mark.
					fputc(0xfe, fo);
					fputc(0xff, fo);
				}
				else
				{
					// Write UTF-16-LE Byte Order Mark.
					fputc(0xff, fo);
					fputc(0xfe, fo);
				}
			}
		}
	}
};

}	// namespace WsuInternal

using WsuInternal::WsuConverter;

int main(int argc, const char **argv)
{
	using namespace std;
	std::string ofilename;
	FILE *fi = NULL;
	FILE *fo = NULL;
	try
	{
		using namespace NetwideCompilerCollection;
		
		std::list<std::string> cmdLine;
		NccCommonUtils::getCommandLine(cmdLine, argc, argv);
		
		for(std::list<std::string>::iterator i = cmdLine.begin(); i != cmdLine.end(); ++i)
		{
			if(*i == "--about")
			{
				print_about();
				return 0;
			}
		}
		
		for(std::list<std::string>::iterator i = cmdLine.begin(); i != cmdLine.end(); ++i)
		{
			if(*i == "--help")
			{
				print_help();
				return 0;
			}
		}
		
		std::list<std::string> options;
		std::list<std::string> files;
		if(!cmdLine.empty())
		{
			std::list<std::string>::iterator i = cmdLine.begin();
			++i;	// skip program name (0th argument)
			for(; i != cmdLine.end(); ++i)
			{
				if(i->empty())
					continue;
				if((*i)[0] == '-')
					options.push_back(*i);
				else
					files.push_back(*i);
			}
		}
		
		// Parse options.
		WsuConverter converter;
		if(!converter.parseOptions(options))
		{
			return 1;
		}

		if(files.size() != 2)
		{
			print_banner();
			print_basic_copyright();
			std::cout << std::endl;
			print_usage();
			std::cout << "For a list of options, use the --help option." << std::endl;
			std::cout << std::endl;
			std::cout << "ERROR: Missing input and/or output file name." << std::endl;
			return 1;
		}
		
		// Make sure we can open in file before we do anything else.
		std::string infilename = files.front();
		fi = NccCommonUtils::universalOpen(infilename.c_str(), "rb");
		if(fi == NULL)
		{
			std::cout << "ERROR: Unable to open file for reading: " << infilename << std::endl;
			return 1;
		}

		// Overwrite (or create) output file.
		ofilename = files.back();
		fo = NccCommonUtils::universalOpen(ofilename.c_str(), "wb");
		
		if(fo == NULL)
		{
			fclose(fi);
			std::cout << "ERROR: Unable to open file for writing: " << ofilename << std::endl;
			return 1;
		}
		
		converter.convert(fi, fo);
	}
	catch(std::exception &e)
	{
		// this should also catch bad alloc's
		std::cout << "fatal error: " << e.what() << std::endl;
		if(fi != NULL)
			fclose(fi);
		if(fo != NULL)
			fclose(fo);
		if(!ofilename.empty())
			NetwideCompilerCollection::NccCommonUtils::removeFile(ofilename.c_str());
		return 1;
	}
	catch(...)
	{
		std::cout << "fatal: unknown internal error" << std::endl;
		if(fi != NULL)
			fclose(fi);
		if(fo != NULL)
			fclose(fo);
		if(!ofilename.empty())
			NetwideCompilerCollection::NccCommonUtils::removeFile(ofilename.c_str());
		return 1;
	}
	
	if(fi != NULL)
		fclose(fi);
	if(fo != NULL)
		fclose(fo);

	return 0;
}
